package edu.kit.csl.pisa.ui;

/*
This file is part of the PISA Alignment Tool.

Copyright (C) 2013
Karlsruhe Institute of Technology
Cognitive Systems Lab (CSL)
Felix Stahlberg

PISA is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PISA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PISA. If not, see <http://www.gnu.org/licenses/>.
*/

import edu.kit.csl.pisa.datatypes.BadParameterValueException;
import edu.kit.csl.pisa.datatypes.Corpus;
import edu.kit.csl.pisa.io.CorpusFactory;
import edu.kit.csl.pisa.io.GizaCorpusFactory;
import edu.kit.csl.pisa.io.GizaModelFactory;
import edu.kit.csl.pisa.io.ModelFactory;
import edu.kit.csl.pisa.io.LazyCorpusFactory;
import edu.kit.csl.pisa.io.LazyModelFactory;
import edu.kit.csl.pisa.io.Logger;
import edu.kit.csl.pisa.io.PisaCorpusFactory;
import edu.kit.csl.pisa.models.AlignmentModel;
import edu.kit.csl.pisa.models.Model3P;
import edu.kit.csl.pisa.models.Model4P;
import edu.kit.csl.pisa.training.EMTrainer;
import edu.kit.csl.pisa.training.Trainer;

/**
 * This class is the main class. It contains the main() method which starts the
 * main program workflow (load resources, train models, output results).
 * 
 * @see #main(String[])
 */
public class Main {

	/**
	 * This method is the main runner method.
	 * @param args
	 */
	public static void main(String[] args) {
		Logger log = Logger.getSingleton();
		// Load configuration
		Configuration conf = Configuration.getSingleton();
		if (args.length == 1 && args[0].equals("-help")) {
			conf.printHelp();
			return;
		}
		conf.processCommandLineArguments(args);
		if (conf.getString("initialModelPath").equals("")) {
			log.fatalError("Parameter initialModelPath must be set.");
		}
		if (log.isFatalErrorRegistered()) {
			log.fatalError("Fatal errors occurred. Use -help to show "
					+ "information about each available parameter");
			return;
		}
		log.notice("Configuration loaded.");
		conf.dumpToFilesystem();
		CorpusFactory factory = createCorpusFactory();
		Corpus corpus = factory.createCorpus();
		log.notice("Corpus loaded (" + corpus.size() + " sentence pairs)");
		if (log.isFatalErrorRegistered()) {
			log.fatalError("Fatal errors occurred while loading the IBM "
					+ "models. Doublecheck the parameter initialModelPath and "
					+ "initialModelLoader and rerun.");
			return;
		}
		startTraining(corpus);
	}
	
	/*
	 * Start training the model hierarchy.
	 */
	private static void startTraining(Corpus corpus) {
		Logger log = Logger.getSingleton();
		try {
			Trainer trainer;
			ModelFactory modelFactory = createModelFactory();
			AlignmentModel initModel = modelFactory.createModel("initialModel");
			log.notice("Initial model loaded.");
			Model3P model3 = new Model3P("model3");
			log.notice("Start parameter import to Model 3P.");
			model3.importModelParameters(initModel);
			initModel = null; // Free space
			trainer = createTrainer(model3);
			log.notice("Start Model 3P training.");
			trainer.train(corpus);
			Model4P model4 = new Model4P("model4");
			log.notice("Start parameter import to Model 4P.");
			trainer = createTrainer(model4);
			model4.importModelParameters(model3);
			model3 = null; // Free space
			log.notice("Start Model 4P training.");
			trainer.train(corpus);
		} catch (BadParameterValueException e) {
			Logger.getSingleton().fatalError("One of the configured "
					+ "parameter has a bad value: " + e.getMessage());
		}
		log.notice("PISA run finished.");
	}
	
	/* (non-Javadoc)
	 * Factory method for creating trainers. Since only EMTrainer is 
	 * implemented now, it unconditionally returns a new EMTrainer instance
	 * 
	 * @see edu.kit.csl.pisa.training.EMTrainer
	 */
	private static Trainer createTrainer(AlignmentModel model)
			throws BadParameterValueException {
		return new EMTrainer(model);
	}

	/*
	 * Creates a corpus factory (GoF design pattern) according to the parameter
	 * initialModelLoader.
	 */
	private static CorpusFactory createCorpusFactory() {
		String l = Configuration.getSingleton().getString("initialModelLoader");
		if (l.equals("giza") || l.equals("min-giza")) {
			return new GizaCorpusFactory();
		} else if (l.equals("min-pisa")) {
			return new PisaCorpusFactory();
		}
		Logger.getSingleton().fatalError("IBM corpus loader " + l
				+ " is not implemented.");
		return new LazyCorpusFactory();
	}
	
	/*
	 * Creates a model factory (GoF design pattern) according to the parameter
	 * initialModelLoader.
	 */
	private static ModelFactory createModelFactory() {
		String l = Configuration.getSingleton().getString("initialModelLoader");
		if (l.equals("giza")) {
			return new GizaModelFactory();
		} else if (l.equals("min-giza") || l.equals("min-pisa")) {
			return new LazyModelFactory();
		}
		Logger.getSingleton().fatalError("IBM model loader " + l
				+ " is not implemented.");
		return new LazyModelFactory();
	}
}
